<?php
namespace WDB\WebUI;
use WDB;

/**
 * HTML node builder.
 * 
 * Provides easy, object-oriented building of HTML code.
 * HTML attributes are retrieved via ArrayAccess,
 * inner content is accessed through ->content property.
 * 
 * @author Richard Ejem <richard(at)ejem.cz>
 * @package WDB
 * @property array $content list of inner nodes(text nodes or elements)
 */
class HTMLNode extends WDB\BaseObject implements \ArrayAccess
{
    /**@var string node name*/
    protected $name;
    /**@var array node attributes*/
    protected $attributes;
    /**@var array node inner content*/
    protected $content;
    /**@var WDB\Utils\ArrayDelegate returned from ->content to return content "by reference" */
    private $contentArrayDelegate;
    /**@var bool if true (default), generate xhtml nodes (terminate non-pair nodes with /> )*/
    protected $xhtml;
    
    /**
     *
     * @param string node name
     * @param array node attribudes
     * @param string|array inner content
     */
    public function __construct($name, $attributes=array(), $content = NULL, $xhtml = true)
    {
        $this->name = $name;
        $this->attributes = $attributes;
        $this->setContent($content);
        $this->xhtml = (bool)$xhtml;
        $this->contentArrayDelegate = new WDB\Utils\ArrayDelegate($this->content);
    }
    
    /**
     * Set __toString output to HTML.
     *
     * @return string
     */
    public function setHtml()
    {
        $this->xhtml = false;
    }
    
    /**
     * Set __toString output to XHTML.
     *
     * @return string
     */
    public function setXhtml()
    {
        $this->xhtml = true;
    }
    
    /**
     * Return HTML representation of this node
     *
     * @return string
     */
    public function html()
    {
        $node = clone $this;
        $node->setHtml();
        return $node->__toString();
    }
    
    /**
     * Return XHTML representation of this node
     *
     * @return string
     */
    public function xhtml()
    {
        $node = clone $this;
        $node->setXhtml();
        return $node->__toString();
    }
    
    /**
     * Get node content. Returned as ArrayDelegate object which represents a reference to an array,
     * so changes to the returned array will be projected to the node.
     *
     * @return WDB\Utils\ArrayDelegate
     */
    public function &getContent()
    {
        return $this->contentArrayDelegate;
    }
    
    /**
     * Set node inner content.
     * 
     * @param string|array Pass list of strings/elements or a string. String represents text node.
     */
    public function setContent($content)
    {
        if ($content === NULL) $content = array();
        elseif (!is_array($content))
        {
            if ($content instanceof WDB\BaseCollection)
            {
                $content = $content->toArray();
            }
            else
            {
                $content = array($content);
            }
        }
        $this->content = $content;
    }
    
    /**
     * Build a HTML string representing this node.
     *
     * @return string
     */
    public function __toString()
    {
        $tag = '<'.htmlspecialchars($this->name);
        foreach ($this->attributes as $key=>$val)
        {
            $tag .= ' '.htmlspecialchars($key).'="'.htmlspecialchars($val).'"';
        }
        if (count($this->content) == 0)
        {
            $tag .= ' />';
            return $tag;
        }
        $tag .= '>';
        foreach ($this->content as $val)
        {
            if ($val instanceof HTMLNode)
            {
                $tag .= $val->__toString();
            }
            else
            {
                $tag .= htmlspecialchars($val);
            }
        }
        $tag .= '</'.htmlspecialchars($this->name).'>';
        return $tag;
    }
    
    /**
     * Removes css class.
     * 
     * @param string CSS class
     * @return HTMLNode fluent interface
     */
    public function removeClass($class)
    {
        if (!empty($this->attributes['class']))
        {
            $classes = explode(' ', $this->attributes['class']);
            WDB\Utils\Arrays::remove($classes, $class);
            $this->attributes['class'] = implode(' ', $classes);
        }
        return $this;
    }
    
    /**
     * Add css class.
     * 
     * @param string CSS class
     * @return HTMLNode fluent interface
     */
    public function addClass($class)
    {
        if (empty($this->attributes['class']))
        {
            $this->attributes['class'] = $class;
        }
        else
        {
            $this->attributes['class'] .= ' '.$class;
        }
        return $this;
    }

    // <editor-fold defaultstate="collapsed" desc="ArrayAccess attribute access">
    public function offsetExists($offset)
    {
        return isset($this->attributes[$offset]);
    }

    public function offsetGet($offset)
    {
        if (!$this->offsetExists($offset)) return NULL;
        return $this->attributes[$offset];
    }

    public function offsetSet($offset, $value)
    {
        $this->attributes[$offset] = $value;
    }

    public function offsetUnset($offset)
    {
        unset($this->attributes[$offset]);
    }
    // </editor-fold>
}
